package flexulous.model;

import flexulous.util.FlexulousCompilerHelper;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.gjt.sp.util.Log;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;

public class FlexulousModel extends Observable
{
	
	Document dom;
    
    public static final int TARGET_REMOVED = 1;
	public static final int TARGET_ADDED = 2;
	
	private String growlLocation;	
	/**
	 * Returns the value of growlLocation.
	 */
	public String getGrowlLocation()
	{
		return growlLocation;
	}

	/**
	 * Sets the value of growlLocation.
	 * @param growlLocation The value to assign growlLocation.
	 */
	public void setGrowlLocation(String growlLocation)
	{
		this.growlLocation = growlLocation;
		File growlFile = new File(growlLocation);
		if(growlFile.exists()){
			growlIsValid = true;
		}
	}

	private boolean growlIsValid;	
	/**
	 * Returns the value of growlIsValid.
	 */
	public boolean getGrowlIsValid()
	{
		return growlIsValid;
	}

	/**
	 * Sets the value of growlIsValid.
	 * @param growlIsValid The value to assign growlIsValid.
	 */
	public void setGrowlIsValid(boolean growlIsValid)
	{
		this.growlIsValid = growlIsValid;
	}

	
	
	
	private String filePath;	
	/**
	 * Returns the value of filePath.
	 */
	public String getFilePath()
	{
		return filePath;
	}

	/**
	 * Sets the value of filePath.
	 * @param filePath The value to assign filePath.
	 */
	public void setFilePath(String filePath)
	{
		this.filePath = filePath;
	}

	
	
	private List<FlexulousTarget> targets;	
	/**
	 * Returns the value of targets.
	 */
	public List<FlexulousTarget> getTargets()
	{
		return targets;
	}

	/**
	 * Sets the value of targets.
	 * @param targets The value to assign targets.
	 */
	public void setTargets(List<FlexulousTarget> targets)
	{
		this.targets = targets;                                
	}

	private List<FlexulousCompilerHelper> helpers;	
	/**
	 * Returns the value of helpers.
	 */
	public List<FlexulousCompilerHelper> getHelpers()
	{
		return helpers;
	}

	/**
	 * Sets the value of helpers.
	 * @param helpers The value to assign helpers.
	 */
	public void setHelpers(List<FlexulousCompilerHelper> helpers)
	{
		this.helpers = helpers;
	}

	public void removeHelper(FlexulousCompilerHelper helper) {
        if (helpers.remove(helper)) {
            if (targets.remove(helper.getTarget())) {
                write();
                setChanged();
                notifyObservers(TARGET_REMOVED);
            }
        }
    }
    
    public void addHelper(FlexulousCompilerHelper helper){
    	helpers.add(helper);
    	targets.add(helper.getTarget());
    	write();
    	setChanged();
    	notifyObservers(TARGET_ADDED);
    }

	public FlexulousModel()
	{
		targets = new ArrayList<FlexulousTarget>();
		helpers = new ArrayList<FlexulousCompilerHelper>();
	}
	
	/**
	 * Attempts to load the model. If the model doesn't exist on disk, it will create it.
	 */
	public void load()
	{
		File file = new File(filePath);
		if(!file.exists()){
			try{
				//This initializes our model for the first time.
				fakeIt(); // Add some fakeIt data.
				write();

			}catch(Exception ex){
				Log.log(Log.WARNING, FlexulousModel.class, "Error in load:"+ex);
			}
		}else{
			Log.log(Log.WARNING, FlexulousModel.class, "Writing to file:"+filePath);
			//Load the model from disk.
			parseXMLFile();
			parseDocument();
		}
		
		
		//And write it back out :P
		write();

	}
	
	
	
	public void write()
	{
		Log.log(Log.WARNING, FlexulousModel.class, "Writing to file:"+filePath);
		
		createDocument();
		createDOMTree();
		printToFile();
	}
	
	private void fakeIt()
	{
		//create our data model.                   
		FlexulousTarget target1 = new FlexulousTarget("Chris Target", "/Users/chris/TestApp.mxml", "Users/chris/TestApp.swf");
		targets.add(target1);
		FlexulousCompilerHelper h1 = new FlexulousCompilerHelper(target1);
		helpers.add(h1);
				
		FlexulousTarget target2 = new FlexulousTarget("Ben Target", "/Users/benslote/TestApp.mxml", "Users/benslote/TestApp.swf");
		targets.add(target2);
		FlexulousCompilerHelper h2 = new FlexulousCompilerHelper(target2);
		helpers.add(h2);
		
	}
	
	private void parseXMLFile(){
		//get the factory
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		
		try {
			
			//Using factory get an instance of document builder
			DocumentBuilder db = dbf.newDocumentBuilder();
			
			//parse using builder to get DOM representation of the XML file
			dom = db.parse(filePath);
			

		}catch(ParserConfigurationException pce) {
			pce.printStackTrace();
		}catch(SAXException se) {
			se.printStackTrace();
		}catch(IOException ioe) {
			ioe.printStackTrace();
		}
	}

		private void parseDocument(){
		//get the root elememt
		Element docEle = dom.getDocumentElement();
		
		//get a nodelist of <target> elements
		NodeList nl = docEle.getElementsByTagName("target");
		if(nl != null && nl.getLength() > 0) {
			for(int i = 0 ; i < nl.getLength();i++) {
				
				//get the target element
				Element el = (Element)nl.item(i);
				
				//get the Target object
				FlexulousTarget e = FlexulousTarget.fromXML(el);
				
				//add it to list
				targets.add(e);
				
				//create a helper.
				FlexulousCompilerHelper h = new FlexulousCompilerHelper(e);
				helpers.add(h);
				Log.log(Log.WARNING, null, "Adding helper:"+h);
			}
		}
	}

	
	
	
	/**
	 * Using JAXP in implementation independent manner create a document object
	 * using which we create a xml tree in memory
	 */
	private void createDocument() {

		//get an instance of factory
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		try {
		//get an instance of builder
		DocumentBuilder db = dbf.newDocumentBuilder();

		//create an instance of DOM
		dom = db.newDocument();

		}catch(ParserConfigurationException pce) {
			//dump it
			Log.log(Log.ERROR, FlexulousModel.class, "Error while trying to instantiate DocumentBuilder " + pce);
		}
	}
	
	/**
	 * The real workhorse which creates the XML structure
	 */
	private void createDOMTree(){

		//create the root element <Books>
		Element rootEle = dom.createElement("flexulous");
		dom.appendChild(rootEle);

		//No enhanced for
		Iterator it  = targets.iterator();
		while(it.hasNext()) {
			FlexulousTarget t = (FlexulousTarget)it.next();
			//For each Target object  create <target> element and attach it to root
			Element targetEle = t.toXML(dom);
			rootEle.appendChild(targetEle);
		}
		
	}
		
		
		
	/**
	 * This method uses Xerces specific classes
	 * prints the XML document to file.
	 */
	private void printToFile(){

		try
		{
			//print
			OutputFormat format = new OutputFormat(dom);
			format.setIndenting(true);

			//to generate a file output use fileoutputstream instead of system.out
			XMLSerializer serializer = new XMLSerializer(
			new FileOutputStream(new File(filePath)), format);

			serializer.serialize(dom);

		} catch(IOException ie) {
		    ie.printStackTrace();
		}
	}


}
